package chans

import (
	"fmt"
	"time"
)

/*
无缓冲通道：编译可以通过，但是执行deadlock
因为无缓冲的通道，在send数据时需要有接收者

发送和接收，至少有一方是异步等待状态
*/
func TestNoBufferReceiveChan() {
	// 创建无缓冲通道
	chanX := make(chan int)

	// 在这里加一个接收操作携程,不然的话会阻塞在通道发送的位置
	go receiver(chanX)

	// 代码会阻塞在这里，只有发送，没有检测到接收
	chanX <- 10

	// 接收方法完成了接收，这里再次接收会出错
	//x := <- chanX

	//<- x
	//fmt.Println(x)

	close(chanX)

	// 稍等一下，让接收操作完成
	time.Sleep(time.Millisecond)
}

func TestNoBufferSendChan() {
	chanX := make(chan int)

	// 发送通道数据携程
	/*
		如果不使用go携程，就会直接发送，但是发送后没有等待的接收方
	*/
	go send(chanX)

	// 阻塞，等待发送通道数据
	x, ok := <-chanX

	if ok {
		fmt.Println("接收到：", x)
	}

	close(chanX)
}

func receiver(c chan int) {
	// 接收值
	i, ok := <-c
	if ok {
		fmt.Println("接收值：i = ", i)
	}
}

func send(chanX chan int) {
	chanX <- 11
}

// --------------------------------------------close
/*
判断一个通道被关闭了
1、返回的ok为false
2、使用for range ch 如果通道被关闭了，会退出range
*/

func TestCloseChan() {
	var ch1 = make(chan int)
	var ch2 = make(chan int)

	// 向ch1中发送数据
	go func() {
		for i := 0; i < 10; i++ {
			ch1 <- i
		}
		// 发送后关闭数据
		close(ch1)
	}()

	// 从ch1中接收数据
	go func() {
		for {
			i, ok := <-ch1
			if !ok {
				break
			}
			// 将数据i发送到ch2
			ch2 <- i * i
		}
		close(ch2)
	}()

	// 从ch2中接收数据
	for i := range ch2 {
		fmt.Println(i)
	}

}

// ----------------------------------------单项通道

/*
单向通道的前面的通道区别在于方法参数的定义上；
方法参数上定义的是单向通道，传递进来的通道的功能就限制为单项通道，成为只能发送的或者只能接收的通道；

双向通道可以根据方法参数转换为单项通道，但是反过来不行

注意：
close方法不能关闭 只能接收的单项通道

*/

// chan<-int 是一个只能发送的通道
func send1(out chan<- int) {
	fmt.Println("send1")
	// 将数据不断的发送给out
	for i := 0; i < 10; i++ {
		// 只能向通道中发送数据
		out <- i
	}
	close(out)
}

// <- chan in 是一个只能接收的通道
// 这里使用in来接收数据，然后再将数据发送到out中，做一个通道的传递
func send2(out chan<- int, in <-chan int) {
	fmt.Println("send2")
	// 在通道in中接收数据
	for i := range in {
		// 将接收到的数据再发送到out中
		fmt.Println("--", i, "--\n")
		out <- i + 1
	}
	close(out)
	//close(in) // close方法关闭发送通道 func close(c chan<- Type)
}

func print(in <-chan int) {
	fmt.Println("print")
	// 使用for range自动跳出
	for i := range in {
		fmt.Println(i)
	}

	//fmt.Println("print")
	//for {
	//	i,ok :=<- in
	//	if !ok {
	//		break
	//	}
	//	fmt.Println(i)
	//}
}

// 测试单项通道传递数据
func TestSingleChan() {
	var ch1 = make(chan int)
	var ch2 = make(chan int)
	// 向ch1中发送数据
	go send1(ch1)
	// 接收ch1，发送到ch2
	go send2(ch2, ch1)

	// 输出ch2数据:在send2中ch2是只能发送的通道，在这里根据参数定义ch2变成只能接收的通道
	go print(ch2)

	// 防止主线程提前结束无法执行携程
	time.Sleep(time.Second)
}
